module(..., package.seeall)

-- 绑定关系记录表(函数执行)
local bindDat = nil

-- 需要绑定的表
local bindTab = nil

-- 实际内容都在此表
local bindTmp = nil

-- 属性设置
function setAttr(func, obj, part, v)
    if type(v) == "nil" or not obj then return end
    if type(v) == "table" then
        if part then
            func(obj, part, lvgl.STATE_DEFAULT, unpack(v))
        else
            func(obj, unpack(v))
        end
    else
        if part then
            func(obj, part, lvgl.STATE_DEFAULT, v)
        else
            func(obj, v)
        end
    end
end

-- 变量绑定
function bind(t)
    bindDat = {}
    bindTmp = {}
    bindTab = t
    setmetatable(bindTab, {
        __newindex = function(t, k, v)
            local item = bindDat[k]
            if type(item) == "table" and item.func then
                setAttr(item.func, item.obj, item.part, v)
            end
            bindTmp[k] = v
            -- 查看哪些值被自动设置
            -- log.info("airui", "meta", k, v)
        end,
        __index = bindTmp
    })
end

-- style 创建 兼容性考虑...
lvgl.style_create = lvgl.style_create or function()
    local s = lvgl.style_t()
    lvgl.style_init(s)
    return s
end

-- 默认容器风格
local cont_style = {
    -- 圆角
    radius = 0,
    -- 背景
    bg_opa        = 0, 
    -- bg_grad_dir   = lvgl.GRAD_DIR_VER, 
    bg_color      = lvgl.color_hex(0xFFFFFF), 
    bg_grad_color = lvgl.color_hex(0xFFFFFF),
    -- 边框
    border_color = lvgl.color_hex(0xFFFFFF), 
    border_opa   = 0,
    border_width = 0,
    -- 内边距
    pad_left   = 0, 
    pad_right  = 0, 
    pad_top    = 0, 
    pad_bottom = 0, 
    pad_inner  = 0,
    -- 外边距
    margin_left   = 0, 
    margin_right  = 0, 
    margin_top    = 0, 
    margin_bottom = 0
}

-- style 需要最先设置
function setStyle(tp, obj, val)
    if type(val) ~= "table" then return end
    -- 获取 part
    local part = lvgl[tp:upper() .. "_PART_MAIN"]
    if not part then
        log.info("airui", "part main 不存在", tp)
        return
    end
    -- 首次使用 style 初始化, 存入表, 下次直接查找
    if not val.style then
        val.style = lvgl.style_create()
        for k, v in pairs(val) do
            local name = "style_set_" .. k
            if lvgl[name] and k ~= "style" then
                log.info("airui", "style", name, v)
                lvgl[name](val.style, part, v)
            end
        end
    end
    lvgl.obj_add_style(obj, part, val.style)
end

-- 对齐需要最后设置
function setAlign(tp, obj, val)
    if type(val) == "table" then
        lvgl.obj_align(obj, nil, unpack(val))
    else
        lvgl.obj_align(obj, nil, val, 0, 0)
    end
end

-- 属性过滤名单
attrList = {
    type = true,
    child = true,
    style = true,
    align = true,
    font = function(obj, val)
        if type(val) ~= "table" or not val.path then return end
        -- 字体只加载一遍, 下次直接设置
        if not val.font then val.font = lvgl.font_load(val.path) end
        lvgl.obj_set_style_local_text_font(obj, lvgl.LABEL_PART_MAIN, lvgl.STATE_DEFAULT, val.font)
    end,
    chart_type = function(obj, val)
        lvgl.chart_set_type(obj, val)
    end,
    chart_series = function(obj, val)
        lvgl.chart_add_series(obj, val)
    end
}

-- 设置容器的一些默认参数
function dftCont(t, par)
    -- 设置默认风格
    t.style = t.style or cont_style
    if par then
        -- t.fit   = t.fit or lvgl.FIT_TIGHT
    else
        t.fit   = t.fit or lvgl.FIT_MAX
    end
    t.align = t.align or lvgl.ALIGN_CENTER
end

-- 加载 page
function load(t, par)
    
    -- 类型判断
    if type(t) ~= "table" then
        log.info("airui", "参数异常")
        return
    end

    -- 容器设置默认风格
    if t.type=="cont" then
        dftCont(t, par)
    end

    par = par or lvgl.scr_act()

    -- 构造函数
    local obj_create = lvgl[(t.type or "") .. "_create"]
    if not obj_create then
        log.info("airui", "类型不存在", t.type)
        return
    end
    -- log.info("airui", "创建", t.type)
    local obj = obj_create(par, nil)
    if not obj then
        log.info("airui", "对象创建失败", t.type)
        return
    end
    -- 先设置风格
    if t.style then setStyle(t.type, obj, t.style) end
    for k, v in pairs(t) do
        attr = type(attrList[k])
        local sfunc = lvgl["obj_set_style_local_" .. (k or "")]
        local func = lvgl[(t.type or "") .. "_set_" .. (k or "")] or lvgl["obj_set_" .. (k or "")]
        local part = nil
        if not func then
            part = lvgl[t.type:upper() .. "_PART_MAIN"]
            func = sfunc
        end

        -- log.info("airui", t.type, k, "obj_set_style_local_" .. (k or ""), t.type:upper() .. "_PART_MAIN")

        if attr == "function" then
            attrList[k](obj, v, func, part)
        elseif attr == "nil" and func then
            -- log.info("airui", t.type, k)
            -- 变量绑定判断
            -- $ 的 ascii 36
            if bindDat and type(v) == "string" and #v > 1 and v:byte(1) == 36 then
                local key = v:sub(2)
                bindDat[key] = {
                    type = t.type,
                    obj = obj,
                    func = func,
                    part = part
                }
                -- 触发一次元表设置
                rawset(bindTmp, key, bindTab[key])
                rawset(bindTab, key, nil)
                bindTab[key] = bindTmp[key]
            else
                setAttr(func, obj, part, v)
            end
        end
    end
    -- 对齐要最后设置
    if t.align then setAlign(t.type, obj, t.align) end
    -- 创建子控件
    for k, v in pairs(t.child or {}) do load(v, obj) end
    return obj
end

-- 删除界面
function del(obj)
    lvgl.obj_del(obj)
    collectgarbage("collect")
end

local now = nil
local obj = nil
local base = nil
local function run(k1, k2)
    if _G[k1] and _G[k1][k2] then
        _G[k1][k2] ()
    elseif _G[base] and _G[base][k2] then
        log.info("airui", "base", k2)
        _G[base][k2] ()
    end
end

function setBase(lbase)
    base = lbase
end

-- 显示界面
function show(id, pageId)
    pageId = pageId or id
    if not page[pageId] then
        log.info("airui", "show return")
        return 
    end
    run(now, "exit")
    if obj then
        airui.del(obj)
        log.info("frame", "删除界面", pageId)
    end
    now = id
    obj = airui.load(page[pageId])
    run(now, "enter")
end

-- 按键消息处理
local function keyEvent(k, e)
    e = e or "Short"
    log.info("keyEvent", k, e, now)
    local ke = k..e
    if _G[now] and _G[now][ke] then
        _G[now][ke]()
    elseif _G[base] and _G[base][ke] then
        _G[base][ke]()
    end
end

-- 按键初始化
function keyInit(a, b, t)
    -- 配置键盘
    local keyTab = {}
    local func = function(id)
        keyTab[id] = "Long"
        keyEvent(id, "Long")
    end
    t = t or {
        [0] = {
            [0] = "keyBoot",
        },
        [2] = {
            [0] = "keyExit",
            [1] = "keyUp",
        },
        [3] = {
            [0] = "keyOk",
            [1] = "keyDown",
        },
        [255] = {
            [255] = "keyPower",
        }
    }
    local tmp = {}
    rtos.on(rtos.MSG_KEYPAD, function(msg)
        local id = (t[msg.key_matrix_row] or tmp)[msg.key_matrix_col]
        -- log.info("key", msg.key_matrix_row, msg.key_matrix_col)
        if not id then return end
        if msg.pressed then
            keyTab[id] = "Short"
            sys.timerStart(func, 300, id)
        else
            sys.timerStop(func, id)
            if  keyTab[id] == "Short" then
                keyEvent(id, "Short")
            elseif  keyTab[id] == "Long" then
                keyEvent(id, "Stop")
            end
            keyTab[id] = nil
        end
    end)
    rtos.init_module(rtos.MOD_KEYPAD, 0, a or 0x1F, b or 0x1F)
end

-- 模拟器兼容性
sys.taskInit(function()
    if not lvgl.indev_get_emu_key then
        return
    end
    while true do
        sys.wait(100)
        local menu, home, back = lvgl.indev_get_emu_key()
        if menu + home + back > 0 then
            if menu==1 then
                keyEvent("keyDown")
            end
            if home==1 then
                keyEvent("keyOk")
            end
            if back==1 then
                keyEvent("keyExit")
            --    keyEvent("keyPower")
            end
            log.info("key", menu, home, back)
        end
    end
end)